package org.carabiner.harness;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.WindowListener;
import java.io.File;

import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JToolBar;

import net.infonode.docking.RootWindow;
import net.infonode.docking.SplitWindow;
import net.infonode.docking.View;
import net.infonode.docking.util.ViewMap;

import org.carabiner.infopanel.Inspector;
import org.carabiner.util.CarabinerProperties;

/**
 * Abstract harness implementation used to hold common code.
 * 
 * <p>
 * Copyright: <a href="http://www.gnu.org/licenses/gpl.html">GNU Public License</a>
 * </p>
 * 
 * @author Ben Rady (benrady@gmail.com)
 * 
 */
abstract class AbstractHarness implements Harness
{
  private JFrame frame;

  private WindowListener windowNotifier;

  private JToolBar actionToolbar;

  private RootWindow rootWindow;

  private Component testSubject;

  private Inspector inspector;

  protected AbstractHarness(Component subject)
  {
    frame = new JFrame();
    windowNotifier = new WindowNotifier(this);
    frame.getContentPane().setLayout(new BorderLayout());

    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.getContentPane().add(getRootWindow(), BorderLayout.CENTER);

    attach(subject);
    InfoPanelAction infoPanelAction = new InfoPanelAction(this);
    addTesterAction(infoPanelAction);
    addTesterAction(new LaunchGroovyConsoleAction(this));
  }

  protected JFrame getFrame()
  {
    return frame;
  }

  public void addTesterAction(Action action)
  {
    if (actionToolbar == null)
    {
      actionToolbar = new JToolBar();
      actionToolbar.setFloatable(false);
      frame.getContentPane().add(actionToolbar, BorderLayout.NORTH);
    }
    JButton button = new JButton(action);
    button.setToolTipText((String)action.getValue(Action.LONG_DESCRIPTION));
    actionToolbar.add(button);
  }

  /**
   * {@inheritDoc}<p/>
   * 
   * 
   * @throws IllegalStateException
   *           if called from the Event Dispatch Thread
   */
  public void showHarness()
  {
    frame.addWindowListener(windowNotifier);
    frame.setLocation(300, 300);
    frame.pack();
    frame.setVisible(true);
  }

  /**
   * Attaches a component to this harness for testing. The component must be
   * attached before showing the harness.
   * 
   * @param subject
   *          Component
   */
  protected void attach(Component subject)
  {
    if (subject == null)
    {
      throw new IllegalArgumentException("Cannot attach a null component");
    }
    testSubject = subject;

    // Init the harness
    frame.setTitle("Testing Component: " + testSubject.getClass().getName());
  }

  public void dispose()
  {
    frame.getContentPane().remove(getRootWindow());
    frame.dispose();
    getRootWindow().close();
  }

  protected RootWindow getRootWindow()
  {
    if (rootWindow == null)
    {
      rootWindow = new RootWindow(new ViewMap());
    }
    return rootWindow;
  }

  void addWindowListener(WindowListener listener)
  {
    getFrame().addWindowListener(listener);
  }

  boolean isVisible()
  {
    return getFrame().isVisible();
  }

  void setVisible(final boolean visible)
  {
    getFrame().setVisible(visible);
  }

  public Inspector getInspector()
  {
    if (inspector == null)
    {
      File rulesFile = new File(CarabinerProperties.getInspectorListFilename());
      if (rulesFile.exists())
      {
        inspector = new Inspector(rulesFile, testSubject);
      }
      else
      {
        inspector = new Inspector(testSubject);
      }
    }
    return inspector;
  }

  public Component getSelectedComponent()
  {
    return inspector.getSelectedComponent();
  }

  public Component getTestSubject()
  {
    return testSubject;
  }

  public void addInfoPanel(String name, JComponent infoPanel, Icon icon)
  {
    View view = new View(name, icon, infoPanel);
    if (rootWindow.getWindow() == null)
    {
      rootWindow.setWindow(view);
    }
    else
    {
      rootWindow.setWindow(new SplitWindow(true, rootWindow.getWindow(), view));            
    }
    frame.pack();
  }

}
